﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WastedMechanics
{
    class SoundManager
    {
        public Dictionary<string, SoundEffect> SoundEffects;
        public Dictionary<string, Music> Musics;

        public List<bool> IsChannelUsed;

        public List<SoundInstance> SoundInstances;

        public FMOD.System FmodSystem;
        public int ChannelsCount = 512;

        public void FmodERRCHECK(FMOD.RESULT result)
        {
            if (result != FMOD.RESULT.OK)
                Mogre.LogManager.Singleton.DefaultLog.LogMessage("FMOD error! " + result + " - " + FMOD.Error.String(result));
        }

        public void InitFmod()
        {
            FMOD.RESULT result = FMOD.Factory.System_Create(ref FmodSystem);
            uint version = 0;
            FmodERRCHECK(result);            

            result = FmodSystem.getVersion(ref version);
            FmodERRCHECK(result);
            if (version < FMOD.VERSION.number)
            {
                Mogre.LogManager.Singleton.DefaultLog.LogMessage("Error!  You are using an old version of FMOD " + version.ToString("X") + ".  This program requires " + FMOD.VERSION.number.ToString("X") + ".");
            }

            FmodSystem.init(ChannelsCount, FMOD.INITFLAGS.NORMAL, (IntPtr)null);

            FmodSystem.set3DSettings(1.0f, 1.0f, 1.0f);
        }

        public SoundManager()
        {
            InitFmod();

            SoundEffects = new Dictionary<string, SoundEffect>();
            Musics = new Dictionary<string, Music>();

            IsChannelUsed = new List<bool>();
            for (int i = 0; i < ChannelsCount; i++) IsChannelUsed.Add(false);

            SoundInstances = new List<SoundInstance>();           
        }

        public void CreateSoundEffect(string filename, bool is3d=true)
        {
            SoundEffects.Add(filename,  new SoundEffect(filename, is3d));
        }

        public void CreateMusic(string filename)
        {
            Musics.Add(filename, new Music(filename));
        }

        public void LoadAllSounds()
        {
            foreach (var snd in SoundEffects.Values) snd.Load();
            foreach (var snd in Musics.Values) snd.Load();
        }

        public void UnloadAllSounds()
        {
            foreach (var snd in SoundEffects.Values) snd.Unload();
            foreach (var snd in Musics.Values) snd.Unload();
        }

        public FMOD.Channel GetChannel(int i)
        {
            FMOD.Channel channel = null;
            Engine.SoundManager.FmodSystem.getChannel(i, ref channel);
            return channel;
        }

        public void FreeChannel(FMOD.Channel ch)
        {
            int i = 0;
            ch.getIndex(ref i);
            IsChannelUsed[i] = false;
        }

        public FMOD.Channel GetFreeChannel()
        {
            int freeChannel = IsChannelUsed.IndexOf(false);
            if (freeChannel != -1)
            {
                IsChannelUsed[freeChannel] = true;
                return GetChannel(freeChannel);
            }
            else
                return null;
        }

        public void Update()
        {
            SoundInstances.ForEach(si =>
                {
                    si.TimePlaying += Engine.FixedTimeStep;
                    if (!si.IsLoop && si.TimePlaying >= si.SoundSource.Length)
                        si.Stop();
                });
            SoundInstances.RemoveAll(si => !si.IsLoop && si.TimePlaying >= si.SoundSource.Length);

            FMOD.VECTOR pos = new FMOD.VECTOR();
            pos.x = Engine.Camera.Position.x;
            pos.y = Engine.Camera.Position.y;
            pos.z = Engine.Camera.Position.z;

            // It have to be reversed, because FMOD swapped left-right channels (why?)
            FMOD.VECTOR forward = new FMOD.VECTOR();
            forward.x =  -Engine.Camera.Direction.x;
            forward.y =  -Engine.Camera.Direction.y;
            forward.z =  -Engine.Camera.Direction.z;

            FMOD.VECTOR up = new FMOD.VECTOR();
            up.x = Engine.Camera.Up.x;
            up.y = Engine.Camera.Up.y;            
            up.z = Engine.Camera.Up.z;

            FMOD.VECTOR vel = new FMOD.VECTOR();
            vel.x = Engine.CameraVelocity.x;
            vel.y = Engine.CameraVelocity.y;
            vel.z = Engine.CameraVelocity.z;

            FmodSystem.set3DListenerAttributes(0, ref pos, ref vel, ref forward, ref up);

            FmodSystem.update();
        }
        
            
    }
}
